package com.wxy.ej.flow.config.filter;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSONObject;
import com.rt.easyjava.common.base.Result;
import com.rt.easyjava.common.base.UserDetailsDto;
import com.rt.easyjava.common.utils.others.JsonUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.micrometer.core.instrument.util.IOUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;

/**
 * filter
 */
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private String[] ignorePaths = new String[]{"/swagger","/webjars/","/csrf/","/v2/","/actuator/health","/coding/form/jwtPortal/"};

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String servletPath = request.getServletPath(); // /swagger-ui.html
        for (int i=0; i<ignorePaths.length; i++){
            String ignorePath = ignorePaths[i];
            if (servletPath.contains(ignorePath)){
                filterChain.doFilter(request, response);
                return;
            }
        }

        String authorization = request.getHeader("Authorization");
        if(authorization==null || (authorization.indexOf("Bearer ")==-1 && authorization.indexOf("ShareLinkJwt ")==-1)){
            filterChain.doFilter(request, response);
            return;
        }

        if (authorization.indexOf("Bearer ")!=-1) {
            String bearerToken = StringUtils.substringAfter(authorization, "Bearer ");
            if (bearerToken == null || bearerToken.trim().length() == 0) {
                filterChain.doFilter(request, response);
                return;
            }
            parseBearerToken(bearerToken, request, response, filterChain);
            return;
        }

        if (authorization.indexOf("ShareLinkJwt ")!=-1) {
            String shareLinkJwt = StringUtils.substringAfter(authorization, "ShareLinkJwt ");
            if (shareLinkJwt == null || shareLinkJwt.trim().length() == 0) {
                filterChain.doFilter(request, response);
                return;
            }
            try{
                parseShareLinkJwt(shareLinkJwt, request, response, filterChain);
                return;
            }catch (ExpiredJwtException e) {
                Result.builder().fault("超链接过期");
                return;
            }
        }

    }

    private void parseBearerToken(String token,
                                  HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain filterChain) throws IOException, ServletException {

        ClassPathResource classPathResource = new ClassPathResource("oauth2.public");
        String publicKey = null;

        publicKey = IOUtils.toString(classPathResource.getInputStream(), Charset.forName("UTF-8"));
        publicKey = publicKey.replace("-----BEGIN PUBLIC KEY-----","");
        publicKey = publicKey.replace("-----END PUBLIC KEY-----","");
        PublicKey pubKey = getPubKey(publicKey);
        Jws<Claims> jws = Jwts.parser()
                .setSigningKey(pubKey)
                .parseClaimsJws(token);

        Claims jwsClaims = jws.getBody();
        Object userObj = jwsClaims.get("user");
        Object dataLimitMap = jwsClaims.get("dataLimitMap"); // {ctcTask:query=10}
        JSONObject userJson = JSONObject.parseObject(JsonUtils.objectToJson(userObj));
        String userId = userJson.getString("userId");
        String name = userJson.getString("name");
        String loginName = userJson.getString("loginName");
        JSONObject dataLimitJson = JSONObject.parseObject(JsonUtils.objectToJson(dataLimitMap));

        MyRequestWrapper parameterRequestWrapper = new MyRequestWrapper(request);
        parameterRequestWrapper.addParameter("_userId", userId);
        parameterRequestWrapper.addParameter("_loginName", loginName);
        parameterRequestWrapper.addParameter("_userName", name);
        parameterRequestWrapper.addParameter("_userJson", userObj.toString());
        parameterRequestWrapper.addParameter("_deptId", userJson.getInteger("departmentId"));
        parameterRequestWrapper.addParameter("_deptCode", userJson.getString("departmentCode"));
        parameterRequestWrapper.addParameter("_deptName", userJson.getString("departmentName"));
        parameterRequestWrapper.addParameter("_companyId", userJson.getInteger("instituteId"));
        parameterRequestWrapper.addParameter("_companyCode", userJson.getString("instituteCode"));
        parameterRequestWrapper.addParameter("_companyName", userJson.getString("instituteName"));
        parameterRequestWrapper.addParameter("_dataLimitMap", JSONUtils.toJSONString(dataLimitJson)); // {"dept:query":10,...}

        // 封装成对象
        UserDetailsDto userDetailsDto = new UserDetailsDto();
        userDetailsDto.setUserId(userId);
        userDetailsDto.setLoginName(loginName);
        userDetailsDto.setUserName(name);
        userDetailsDto.setUserJson(userObj.toString());
        userDetailsDto.setDeptId(userJson.getInteger("departmentId").toString());
        userDetailsDto.setDeptCode(userJson.getString("departmentCode"));
        userDetailsDto.setDeptName(userJson.getString("departmentName"));
        userDetailsDto.setCompanyId(userJson.getInteger("instituteId").toString());
        userDetailsDto.setCompanyCode(userJson.getString("instituteCode"));
        userDetailsDto.setCompanyName(userJson.getString("instituteName"));
        userDetailsDto.setDataLimitMap(JSONUtils.toJSONString(dataLimitJson));
        parameterRequestWrapper.addParameter("userDetailsJson", JSONObject.toJSONString(userDetailsDto));

        filterChain.doFilter(parameterRequestWrapper, response);// 替换原始请求参数
    }

    @Value("${jwt.shareLink}")
    private String signature;

    private void parseShareLinkJwt(String shareLinkJwt,
                                  HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain filterChain) throws IOException, ServletException {
        System.out.println("parseShareLinkJwt");
        System.out.println(signature);
        Claims claims = Jwts.parser()
                .setSigningKey(signature)
                .parseClaimsJws(shareLinkJwt)
                .getBody();

        String dynamicFormId = (String) claims.get("dynamicFormId");
        String expandData = (String) claims.get("expandData");

        System.out.println(dynamicFormId);
        System.out.println(expandData);
        MyRequestWrapper parameterRequestWrapper = new MyRequestWrapper(request);
        parameterRequestWrapper.addParameter("shareLinkDynamicFormId", dynamicFormId);
        parameterRequestWrapper.addParameter("shareLinkExpandData", expandData);

        filterChain.doFilter(parameterRequestWrapper, response);
    }

    /**
     * 实例化公钥
     *
     * @return
     */
    private PublicKey getPubKey(String pubKey) {
        PublicKey publicKey = null;
        try {
            java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
                    Base64.decodeBase64(pubKey));
            // RSA对称加密算法
            java.security.KeyFactory keyFactory;
            keyFactory = java.security.KeyFactory.getInstance("RSA");
            // 取公钥匙对象
            publicKey = keyFactory.generatePublic(bobPubKeySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return publicKey;
    }

}
